﻿using System;
using System.Collections.Generic;

namespace IntoNails.Infrastructure.DataAccess
{
    public abstract class AbstractDataRepository<T, TK> : IDataRepository<T, TK>
    {
        public event EventHandler<DataEventArgs<T>>  Added;
        public event EventHandler<DataEventArgs<T>> Removed;
        public event EventHandler<DataUpdatedEventArgs<T>> Updated;

        protected List<T> InnerDataList { get; set; }

        public abstract T Get(TK uniqueCode);
        public abstract IList<T> GetRawItemList();
        public abstract IEnumerable<T> Get(Func<T, bool> predicate);
        public abstract void Add(T item);
        public abstract void Remove(TK uniqueCode);
        public abstract void Update(TK oldItemKey, T newItem);
        public abstract void Save();
        public abstract int Load();
        public abstract void Initialize();

        protected virtual void OnDataAdded(DataEventArgs<T> e)
        {
            var handler = this.Added;
            if (null != handler)
            {
                handler.BeginInvoke(this, e, this.OnDataAddedCompleted, handler);
            }
        }
        protected virtual void OnDataAddedCompleted(IAsyncResult result)
        {
            var handler = result.AsyncState as EventHandler<DataEventArgs<T>>;
            if (null != handler)
            {
                handler.EndInvoke(result);
            }
        }

        protected virtual void OnDataRemoved(DataEventArgs<T> e)
        {
            var handler = this.Removed;
            if (null != handler)
            {
                Removed.BeginInvoke(this, e, this.OnDataRemovedCompleted, handler);
            }
        }
        protected virtual void OnDataRemovedCompleted(IAsyncResult result)
        {
            var handler = result.AsyncState as EventHandler<DataEventArgs<T>>;
            if (null != handler)
            {
                handler.EndInvoke(result);
            }
        }

        protected virtual void OnDataUpdated(DataUpdatedEventArgs<T> e)
        {
            var handler = this.Updated;
            if (null != handler)
            {
                handler.BeginInvoke(this, e, this.OnDataUpdatedCompleted, handler);
            }
        }
        protected virtual void OnDataUpdatedCompleted(IAsyncResult result)
        {
            var handler = result.AsyncState as EventHandler<DataUpdatedEventArgs<T>>;
            if (null != handler)
            {
                handler.EndInvoke(result);
            }
        }
    }

    public class DataEventArgs<T> : EventArgs
    {
        public DataEventArgs(bool result)
        {
            this.Result = result;
        }

        public DataEventArgs(Exception error)
        {
            this.Error = error;
        }

        public DataEventArgs(T item, bool result)
        {
            this.Item = item;
            this.Result = result;
        }

        public T Item { get; private set; }

        public bool Result { get; private set; }

        public Exception Error { get; private set; }
    }

    public class DataUpdatedEventArgs<T> : EventArgs
    {
        public DataUpdatedEventArgs(bool result)
        {
            this.Result = result;
        }

        public DataUpdatedEventArgs(Exception error)
        {
            this.Error = error;
        }

        public DataUpdatedEventArgs(T oldItem, T newItem, bool result, Exception error)
        {
            this.OldItem = oldItem;
            this.NewItem = newItem;
            this.Result = result;
            this.Error = error;
        }

        public T NewItem { get; private set; }

        public T OldItem { get; private set; }

        public bool Result { get; private set; }

        public Exception Error { get; private set; }
    }
}
